package xyz.hlh.boot6.log;

import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import xyz.hlh.boot6.anno.MyLog;

import java.lang.reflect.Method;

/**
 * @author HLH
 * @description: 日志aop拦截
 * @email 17703595860@163.com
 * @date : Created in 2021/10/12 12:34
 */
@Component // 注册组件
@Aspect // 开启aop
@Slf4j(topic = "MyLog") // 修改logger name
public class MyLogAspect {

    /**
     * 拦截标注了MyLog自定义注解的类或者方法
     */
    @Pointcut(value = "@within(xyz.hlh.boot6.anno.MyLog) || @annotation(xyz.hlh.boot6.anno.MyLog)")
    public void pointCut() {}

    /**
     * 前置打印
     */
    @Before(value = "pointCut()")
    public void before(JoinPoint joinPoint) {
        // 判断是否需要打印日志
        if (!checkPrintLog(joinPoint)) return;

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取方法名
        String methodName = signature.getName();
        // 获取类全限定类名
        String declaringTypeName = signature.getDeclaringTypeName();
        // 获取类名
        String className = declaringTypeName.substring(declaringTypeName.lastIndexOf(".") + 1);
        // 记录日志
        Object[] args = joinPoint.getArgs();
        log.info("{}.{} is called, args list is {}", className, methodName, JSONUtil.toJsonStr(args));
    }

    /**
     * 后置打印
     */
    @AfterReturning(value = "pointCut()", returning = "returnObj")
    public void afterReturning(JoinPoint joinPoint, Object returnObj) {
        // 判断是否需要打印日志
        if (!checkPrintLog(joinPoint)) return;

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取方法名
        String methodName = signature.getName();
        // 获取类全限定类名
        String declaringTypeName = signature.getDeclaringTypeName();
        // 获取类名
        String className = declaringTypeName.substring(declaringTypeName.lastIndexOf(".") + 1);
        // 记录日志
        log.info("{}.{} is call to complete, return value is {}", className, methodName, JSONUtil.toJsonStr(returnObj));
    }

    /**
     * 异常打印
     */
    @AfterThrowing(value = "pointCut()", throwing = "e")
    public void afterThrowing(JoinPoint joinPoint, Exception e) {
        // 判断是否需要打印日志
        if (!checkPrintLog(joinPoint)) return;

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取方法名
        String methodName = signature.getName();
        // 获取类全限定类名
        String declaringTypeName = signature.getDeclaringTypeName();
        // 获取类名
        String className = declaringTypeName.substring(declaringTypeName.lastIndexOf(".") + 1);
        // 记录日志
        log.error("{}.{} throws exception '{}' because of '{}'"
                , className, methodName, e.getClass(), e.getMessage(), e);
    }

    /**
     * 获取标注注解的方法是否要打印日志
     * @param joinPoint JoinPoint
     * @return 返回注解的value值
     */
    private boolean checkPrintLog(JoinPoint joinPoint) {
        // 获取方法或者类上的MyLog注解
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        MyLog myLog = method.getAnnotation(MyLog.class);
        if (myLog == null) {
            Class<?> aClass = joinPoint.getTarget().getClass();
            myLog = aClass.getAnnotation(MyLog.class);
        }
        // 获取是否打印日志
        return myLog.value();
    }

}
